﻿using Framework.Wechat.HttpClients;
using Framework.Wechat.Repositories;
using Framework.WePay.Services.HttpClients;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using SKIT.FlurlHttpClient.Wechat.Api.Models;
using SKIT.FlurlHttpClient.Wechat.Api;
using Framework.Wechat.Options;
using Framework.WePay.Options;
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Models;
using SKIT.FlurlHttpClient.Wechat.TenpayV3;
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using Newtonsoft.Json;
using static SKIT.FlurlHttpClient.Wechat.Api.Models.WxaSecOrderUploadCombinedShippingInfoRequest.Types.SubOrder.Types;
using Microsoft.AspNetCore.Authorization;
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Events;
using System.Net;
using System.Text;

namespace WxPayDemo.Webapi.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class WxDemoController : ControllerBase
    {
        private readonly ILogger<WxDemoController> _logger;

        //微信支付接口
        private readonly IWechatTenpayHttpClientFactory _tenpayHttpClientFactory;

        //微信小小程序接口
        private readonly IWechatApiHttpClientFactory _wechatApiHttpClientFactory;

        //微信获取AccessToken接口
        private readonly IWechatAccessTokenEntityRepository _wechatAccessTokenEntityRepository;

        //获取微信支付配置文件内容
        private readonly TenpayOptions _tenpayOptions;

        //获取微信小程序配置文件内容
        private readonly WechatOptions _wechatOptions;

        public WxDemoController(ILogger<WxDemoController> logger, IWechatTenpayHttpClientFactory wechatTenpayHttpClientFactory, IWechatApiHttpClientFactory wechatApiHttpClientFactory, IWechatAccessTokenEntityRepository wechatAccessTokenEntities, IOptions<TenpayOptions> tenpayOptions, IOptions<WechatOptions> wechatOptions)
        {
            _logger = logger;
            _tenpayHttpClientFactory = wechatTenpayHttpClientFactory;
            _wechatApiHttpClientFactory = wechatApiHttpClientFactory;
            _wechatAccessTokenEntityRepository = wechatAccessTokenEntities;
            _tenpayOptions = tenpayOptions.Value;
            _wechatOptions = wechatOptions.Value;
        }

        [HttpGet]
        public async Task<IActionResult> Test()
        {
            return Ok("隧道测试");
        }

        /// <summary>
        /// 根据前端传入的code获取openid
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        [HttpGet]
        public async Task<IActionResult> GetOpenid(string code)
        {
            //获取AccessToken
            var entity = _wechatAccessTokenEntityRepository.FirstOrDefault();
            //创建请求体
            var request = new SnsJsCode2SessionRequest() { AccessToken = entity?.AccessToken, JsCode = code };
            //创建请求客户端
            var client = _wechatApiHttpClientFactory.Create();
            var res = await client.ExecuteSnsJsCode2SessionAsync(request);
            //获取成功
            if (res.ErrorCode == 0)
            {
                return Ok(res.OpenId);
            }
            else
            {
                return Ok("获取用户openid失败" + "errcode:" + res.ErrorCode + "  errmsg:" + res.ErrorMessage);
            }
        }

        /// <summary>
        /// 创建订单
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public async Task<IActionResult> CreatorOrder()
        {
            //创建一个请求客户端
            var client = _tenpayHttpClientFactory.Create();
            //获取微信小程序的配置文件内容
            var wechatAccountOptions = _wechatOptions.Accounts?.FirstOrDefault();
            var request = new CreatePayTransactionJsapiRequest()
            {
                OutTradeNumber = "12200213121231",//设置订单号，一般是传入自己内部系统的订单号
                AppId = wechatAccountOptions.AppId,//微信小程序的Appid
                Description = "测试商品",//商品名称
                NotifyUrl = _tenpayOptions.SucceedNotifyUrl,//这个是配置文件里面写的支付成功的回调地址，必须是https
                Amount = new CreatePayTransactionJsapiRequest.Types.Amount() { Total = 100 },//设置金额 注意单位是分
                Payer = new CreatePayTransactionJsapiRequest.Types.Payer() { OpenId = "sdfsdf324sdfsd" }//这里是微信用户的opnid，就是这个订单是哪个用户的，就是传入该用户的openid
            };
            //发送请求
            var response = await client.ExecuteCreatePayTransactionJsapiAsync(request, cancellationToken: HttpContext.RequestAborted);
            //当请求不成功
            if (!response.IsSuccessful())
            {
                _logger.LogWarning("JSAPI 下单失败（状态码：{0}，错误代码：{1}，错误描述：{2}）。", response.RawStatus, response.ErrorCode, response.ErrorMessage);
                //自己处理自己系统的业务逻辑
                return Ok("JSAPI 下单失败");
            }
            _logger.LogWarning("JSAPI 成功 预支付交易会话标识：{0}", response.PrepayId);
            //下单成功以后自己处理自己系统内部的业务逻辑
            //下面的代码是推送相应的数据给前端，前端根据这些数据调起支付接口
            List<IDictionary<string, string>> keyValuePairs = new List<IDictionary<string, string>>();
            //传入小程序的appid及微信返回的预支付ID获取想要返回给前端的数据
            var paramMap = client.GenerateParametersForJsapiPayRequest(request.AppId, response.PrepayId);
            keyValuePairs.Add(paramMap);
            IDictionary<string, string> orderdic = new Dictionary<string, string> { { "orderid", "这里我们可以把内部系统的订单ID传给前端，方便处理业务" } };
            keyValuePairs.Add(orderdic);
            return Ok(keyValuePairs);
        }

        /// <summary>
        /// 未付款的订单付款
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public async Task<IActionResult> OrderPay()
        {
            //未付款的订单付款是有时效性的，我记得好像是2个小时 120分钟
            //创建微信支付客户端
            var client = _tenpayHttpClientFactory.Create();
            //获取微信小程序的配置文件
            var wechatAccountOptions = _wechatOptions.Accounts?.FirstOrDefault();
            //获取支付相关数据 然后把这些数据给前端 前端发起支付
            var paramMap = client.GenerateParametersForJsapiPayRequest(wechatAccountOptions.AppId, "这里传入微信支付创建订单的时候返回的微信订单号 就是 :PrepayId");
            return Ok(paramMap);
        }

        /// <summary>
        /// 查询订单的支付状态
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public async Task<IActionResult> QueryDisposeOrderAlipayState()
        {
            //读取微信支付配置文件
            var tenpayAccountOptions = _tenpayOptions.Merchants?.FirstOrDefault();
            //创建微信支付客户端
            var client = _tenpayHttpClientFactory.Create();
            //创建请求数据
            var request = new GetPayTransactionByIdRequest();
            request.MerchantId = tenpayAccountOptions.MerchantId;//这里是商户号
            request.TransactionId = "4200001847202306199463758632";
            //发起请求
            var response = await client.ExecuteGetPayTransactionByIdAsync(request);
            if (response.IsSuccessful())
            {
                var eventType = response.TradeState?.ToUpper();
                //当支付支付成功
                if (eventType == "SUCCESS")
                {
                    //处理内部业务
                    //微信支付订单号
                    var payno = response.TransactionId;
                    //支付时间
                    var paytime = ((DateTimeOffset)response.SuccessTime).DateTime;
                    _logger.LogInformation("查询到微信支付推送的订单支付成功，支付时间：{0}", ((DateTimeOffset)response.SuccessTime).DateTime);
                    //实际支付金额
                    var paymoney = (decimal)response.Amount.PayerTotal / 100;
                    _logger.LogInformation("查询到微信支付推送的订单支付成功，支付金额 单位分：{0}", response.Amount.PayerTotal);
                    return Ok("状态码：" + eventType + "，消息：" + response.TradeStateDescription);
                }
                else//当查询到的状态是支付不成功
                {
                    //处理系统内部业务
                    return Ok("状态码：" + eventType + "，消息：" + response.TradeStateDescription);
                }
            }
            else
            {
                return Ok("查询失败 状态码：" + response.RawStatus + "，错误代码：" + response.ErrorCode + "，错误描述" + response.ErrorMessage);
            }
        }

        /// <summary>
        /// 订单退款
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public async Task<IActionResult> OrderRefund()
        {
            //创建微信支付客户端
            var client = _tenpayHttpClientFactory.Create();
            //订单退款请求体
            var request = new CreateRefundDomesticRefundRequest()
            {
                //transaction_id
                TransactionId = "这里传入微信支付创建订单的时候返回的微信订单号 就是 :PrepayId",
                //商户退款单号
                OutRefundNumber = "这里传入自己内部系统的退款单号",
                Amount = new CreateRefundDomesticRefundRequest.Types.Amount()
                {
                    Total = 100,//这里是订单的金额 单位是分
                    Refund = 100 //这里是退款的金额 单位是分，退款可以全部退 也可以退一部分
                },
                Reason = "这里是退款的备注",
                NotifyUrl = _tenpayOptions.FailNotifyUrl//这里是读取配置文件里面的退款成功回调
            };
            //发起退款请求
            var response = await client.ExecuteCreateRefundDomesticRefundAsync(request, cancellationToken: HttpContext.RequestAborted);
            if (response.IsSuccessful())
            {
                //这里只是发起退款申请成功，并不能说明退款已经成功，退款是否成功 要在回调里面判断 或者主动去查询
                _logger.LogError("申请退款成功（返回信息：{0}。", response);
                return Ok("申请退款成功");
            }
            else
            {
                _logger.LogError("申请退款失败（状态码：{0}，错误代码：{1}，错误描述：{2}）。", response.RawStatus, response.ErrorCode, response.ErrorMessage);
                return Ok("申请退款失败");
            }
        }

        /// <summary>
        /// 查询订单退款状态
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public async Task<IActionResult> QueryOrderRefund()
        {
            //创建微信支付客户端
            var client = _tenpayHttpClientFactory.Create();
            //请求数据
            var request = new GetRefundDomesticRefundByOutRefundNumberRequest();
            request.OutRefundNumber = "这里填写自己系统内部的退款单号";  //OutRefundNumber = "这里传入自己内部系统的退款单号",  就是上一个微信退款申请的时候 我们这里的这个单号
            //发起请求
            var response = await client.ExecuteGetRefundDomesticRefundByOutRefundNumberAsync(request);
            //查询成功
            if (response.IsSuccessful())
            {
                //获取退款的状态码
                var State = response.Status?.ToUpper();
                //当退款成功
                if (State == "SUCCESS")
                {
                    var SuccessTime = ((DateTimeOffset)response.SuccessTime).DateTime; //退款成功时间
                    var Refund = (decimal)response.Amount.Refund; //退款成功金额 单位是分
                    //开始处理业务
                    return Ok("退款成功");
                }
                else
                {
                    return Ok("退款失败");
                }
            }
            else
            {
                return Ok("查询失败");
            }
        }

        /// <summary>
        /// 支付成功回调接口
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> ReceiveSucceedMessage(
            [FromHeader(Name = "Wechatpay-Timestamp")] string timestamp,
            [FromHeader(Name = "Wechatpay-Nonce")] string nonce,
            [FromHeader(Name = "Wechatpay-Signature")] string signature,
            [FromHeader(Name = "Wechatpay-Serial")] string serialNumber)
        {
            using var reader = new StreamReader(Request.Body, Encoding.UTF8);
            string content = await reader.ReadToEndAsync();
            _logger.LogInformation("接收到微信支付推送的数据：{0}", content);
            var client = _tenpayHttpClientFactory.Create();
            bool valid = client.VerifyEventSignature(
               callbackTimestamp: timestamp,
               callbackNonce: nonce,
               callbackBody: content,
               callbackSignature: signature,
               callbackSerialNumber: serialNumber
           );
            //验证签名
            if (!valid)
            {
                // NOTICE:
                //   需提前注入 CertificateManager、并下载平台证书，才可以使用扩展方法执行验签操作。
                //   请参考本示例项目 TenpayCertificateRefreshingBackgroundService 后台任务中的相关实现。
                //   有关 CertificateManager 的完整介绍请参阅《开发文档 / 基础用法 / 如何验证回调通知事件签名？》。
                //   后续如何解密并反序列化，请参阅《开发文档 / 基础用法 / 如何解密回调通知事件中的敏感数据？》。
                _logger.LogInformation("验签失败", content);
                return new JsonResult(new { code = "FAIL", message = "验签失败" });
            }
            //解密数据
            var callbackModel = client.DeserializeEvent(content);
            var eventType = callbackModel.EventType?.ToUpper();
            //当支付成功
            if (eventType == "TRANSACTION.SUCCESS")
            {
                //处理自己系统的业务
                var callbackResource = client.DecryptEventResource<TransactionResource>(callbackModel);
                _logger.LogInformation("接收到微信支付推送的订单支付成功通知，商户订单号：{0}", callbackResource.OutTradeNumber);
                _logger.LogInformation("接收到微信支付推送的订单支付成功通知，微信支付订单号：{0}", callbackResource.TransactionId);
                //订单号
                var OutTradeNumber = callbackResource.OutTradeNumber;
                //微信支付订单号
                var Payno = callbackResource.TransactionId;
                //支付时间
                var Paytime = callbackResource.SuccessTime.DateTime;
                _logger.LogInformation("接收到微信支付推送的订单支付成功通知，支付时间：{0}", callbackResource.SuccessTime.DateTime);
                //实际支付金额
                var Paymoney = (decimal)callbackResource.Amount.PayerTotal;//单位是分
                _logger.LogInformation("接收到微信支付推送的订单支付成功通知，支付金额 单位分：{0}", callbackResource.Amount.PayerTotal);
                return new JsonResult(new { code = "SUCCESS", message = "支付成功" });
            }
            else
            {
                // 其他情况略
                _logger.LogInformation("支付回调发生严重错误eventType不等于TRANSACTION.SUCCESS：{0}", eventType);
                return new JsonResult(new { code = "FAIL", message = "eventType不等于TRANSACTION.SUCCESS" });
            }
        }

        /// <summary>
        /// 退款成功回调接口
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> RefundSucceedMessage(
            [FromHeader(Name = "Wechatpay-Timestamp")] string timestamp,
            [FromHeader(Name = "Wechatpay-Nonce")] string nonce,
            [FromHeader(Name = "Wechatpay-Signature")] string signature,
            [FromHeader(Name = "Wechatpay-Serial")] string serialNumber
         )
        {
            using var reader = new StreamReader(Request.Body, Encoding.UTF8);
            string content = await reader.ReadToEndAsync();
            _logger.LogInformation("接收到微信支付推送的退款通知数据：{0}", content);
            var client = _tenpayHttpClientFactory.Create();
            bool valid = client.VerifyEventSignature(
               callbackTimestamp: timestamp,
               callbackNonce: nonce,
               callbackBody: content,
               callbackSignature: signature,
               callbackSerialNumber: serialNumber
           );
            //验证签名
            if (!valid)
            {
                // NOTICE:
                //   需提前注入 CertificateManager、并下载平台证书，才可以使用扩展方法执行验签操作。
                //   请参考本示例项目 TenpayCertificateRefreshingBackgroundService 后台任务中的相关实现。
                //   有关 CertificateManager 的完整介绍请参阅《开发文档 / 基础用法 / 如何验证回调通知事件签名？》。
                //   后续如何解密并反序列化，请参阅《开发文档 / 基础用法 / 如何解密回调通知事件中的敏感数据？》。
                _logger.LogInformation("验签失败", content);
                return new JsonResult(new { code = "FAIL", message = "验签失败" });
            }
            var callbackModel = client.DeserializeEvent(content);

            var eventType = callbackModel.EventType?.ToUpper();
            //解密数据
            var callbackResource = client.DecryptEventResource<RefundResource>(callbackModel);
            //退款成功通知
            if (eventType == "REFUND.SUCCESS")
            {
                //获取状态码
                var State = callbackResource.RefundStatus?.ToUpper();
                //订单号
                var OrderId = long.Parse(callbackResource.OutTradeNumber);
                //退款单号
                var OrderRefId = long.Parse(callbackResource.OutRefundNumber);
                //微信支付的退款单号
                var WxOrderRefId = callbackResource.RefundId;
                //当退款成功
                if (State == "SUCCESS")
                {
                    //处理自己系统内部业务
                    //退款成功时间
                    var SuccessTime = ((DateTimeOffset)callbackResource.SuccessTime).DateTime;
                    //退款成功金额 单位分
                    var Refund = (decimal)callbackResource.Amount.Refund;
                    _logger.LogInformation("退款成功通知：{0}", callbackResource);
                    return new JsonResult(new { code = "SUCCESS", message = "成功" });
                }
                else
                {
                    //当不成功的时候处理业务
                    _logger.LogInformation("退款成功通知：{0}", callbackResource);
                    return new JsonResult(new { code = "SUCCESS", message = "成功" });
                }
            }
            else//其他情况重新查询退款单
            {
                //其他情况自己处理内部系统业务
                return new JsonResult(new { code = "SUCCESS", message = "成功" });
            }
        }
    }
}